#include <utils/Describe.h>
#include <utils/Map.h>

#include <boost/unordered/concurrent_flat_map.hpp>
#include <unordered_map>

#define PP_CONCAT(A, B) PP_CONCAT_IMPL(A, B)
#define PP_CONCAT_IMPL(A, B) A##B

#define PRIVATE_MEMBER_MAP_NAME PP_CONCAT(private_member_map_, CURRENT_CLASS_NAME)

#define PRIVATE_MEMBER_MAP_CONST_NAME PP_CONCAT(private_member_map_const_, CURRENT_CLASS_NAME)

#define BOOST_DESCRIBE_MEMBER_TYPE_NAME PP_CONCAT(D1_, CURRENT_CLASS_NAME)

#define SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_MAP_FUNC                                              \
    PP_CONCAT(initPrivateMemberMap, CURRENT_CLASS_NAME)

#define SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_MAP_FUNC_CONST                                        \
    PP_CONCAT(SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_MAP_FUNC, Const)

#define SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_INDEX_FUNC                                            \
    PP_CONCAT(initPrivateMemberIndex, CURRENT_CLASS_NAME)

#define SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_STRUCT                                                \
    PP_CONCAT(CURRENT_CLASS_NAME, PrivateMemberMapInit)

#define SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_STRUCT_NAME                                           \
    PP_CONCAT(tmp_, SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_STRUCT)

namespace solar::internal::describe
{
    using BOOST_DESCRIBE_MEMBER_TYPE_NAME =
        boost::describe::describe_members<std::remove_reference_t<CURRENT_CLASS_NAME>,
                                          boost::describe::mod_any_access>;
    static boost::unordered::concurrent_flat_map<std::string_view, std::uint64_t>
        PRIVATE_MEMBER_MAP_NAME;

    consteval auto SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_INDEX_FUNC()
        -> std::array<const char*, boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value>
    {
        std::array<const char*, boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value> vec{};
        std::uint64_t index = 0;
        boost::mp11::mp_for_each<BOOST_DESCRIBE_MEMBER_TYPE_NAME>(
            [&](auto&& D)
            {
                vec[index] = D.name;
                index++;
            });
        return vec;
    }

    consteval auto SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_MAP_FUNC_CONST()
        -> const_expr::Map<std::string_view, std::uint64_t,
                           boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value>
    {
        if constexpr (boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value == 0)
        {
            return {};
        }
        else
        {
            auto array = SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_INDEX_FUNC();
            std::array<std::pair<std::string_view, std::uint64_t>,
                       boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value>
                map_data;
            std::uint64_t index = 0;
            for (const auto* value : array)
            {
                map_data[index] = std::make_pair(std::string_view(value), index);
                index++;
            }
            return {map_data};
        }
    }

    constexpr const_expr::Map<std::string_view, std::uint64_t,
                              boost::mp11::mp_size<BOOST_DESCRIBE_MEMBER_TYPE_NAME>::value>
        PRIVATE_MEMBER_MAP_CONST_NAME = SOLAR_REGISTER_CLASS_MEMBER_DESCRIBE_MAP_FUNC_CONST();

    template <>
    inline auto getPrivateMemberIndex<CURRENT_CLASS_NAME>()
        -> boost::unordered::concurrent_flat_map<std::string_view, std::uint64_t>*
    {
        return std::addressof(PRIVATE_MEMBER_MAP_NAME);
    }

    template <> consteval auto getPrivateMemberIndexConst<CURRENT_CLASS_NAME>()
    {
        return PRIVATE_MEMBER_MAP_CONST_NAME;
    }
} // namespace solar::internal::describe
